package com.ljh.lottery.service.mq;

import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.ljh.lottery.common.exception.ServiceException;
import com.ljh.lottery.common.utils.BeanTransUtil;
import com.ljh.lottery.common.utils.JacksonUtil;
import com.ljh.lottery.common.utils.MailUtil;
import com.ljh.lottery.common.utils.SMSUtil;
import com.ljh.lottery.mapper.ActivityPrizeMapper;
import com.ljh.lottery.mapper.WinningRecordMapper;
import com.ljh.lottery.pojo.dataobject.ActivityPrizeDO;
import com.ljh.lottery.pojo.dataobject.WinningRecordDO;
import com.ljh.lottery.pojo.request.DrawPrizeRequest;
import com.ljh.lottery.service.DrawPrizeService;
import com.ljh.lottery.service.activitystatus.ActivityStatusManager;
import com.ljh.lottery.service.dto.ConvertActivityStatusDTO;
import com.ljh.lottery.service.enums.ActivityPrizeStatusEnum;
import com.ljh.lottery.service.enums.ActivityPrizeTiersEnum;
import jakarta.annotation.Resource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.util.List;
import java.util.Map;
import java.util.Objects;

import static com.ljh.lottery.common.config.DirectRabbitConfig.QUEUE_NAME;

/**
 * @author: Themberfue
 * @date: 2025/4/16 18:34
 * @description:
 */
@Component
@RabbitListener(queues = QUEUE_NAME)
public class MqReceiver {

    private static final Logger log = LoggerFactory.getLogger(MqReceiver.class);

    @Resource(name = "drawPrizeServiceImpl")
    private DrawPrizeService drawPrizeService;

    @Resource(name = "activityStatusManagerImpl")
    private ActivityStatusManager activityStatusManager;

    @Resource(name = "asyncServiceExecutor")
    private ThreadPoolTaskExecutor threadPoolTaskExecutor;

    @Autowired
    private SMSUtil smsUtil;

    @Autowired
    private MailUtil mailUtil;

    @Autowired
    ActivityPrizeMapper activityPrizeMapper;

    @Autowired
    WinningRecordMapper winningRecordMapper;

    @RabbitHandler
    public void process(Map<String, String> message) {
        // TODO 1. 打印日志
        log.info("MQ成功接收到消息: message = {}",
                JacksonUtil.writeValueAsString(message));

        // TODO 2. 获取存放在队列的请求数据
        String paramData = message.get("messageData");
        DrawPrizeRequest param = JacksonUtil.readValue(paramData, DrawPrizeRequest.class);

        try {
            // TODO 3. 检验抽奖请求是否有效
            if (!drawPrizeService.checkDrawPrizeParam(param)) {
                return;
            }

            // TODO 4. 状态扭转
            statusConvert(param);

            // TODO 5. 保存中奖者名单
            List<WinningRecordDO> winningRecordDOS = drawPrizeService.saveWinnerRecords(param);

            // TODO 6. 通知中奖者（短信，邮箱）（异步发送）
            syncExecute(winningRecordDOS);

        } catch (ServiceException e) {
            log.error("处理 MQ 消息异常！{}:{}", e.getCode(), e.getMessage(), e);
            // TODO 7. 异常处理，回滚事务
            rollback(param);

            throw e;
        } catch (Exception e) {
            log.error("处理 MQ 消息异常！", e);
            // TODO 7. 异常处理，回滚事务
            rollback(param);

            throw e;
        }
    }

    private void rollback(DrawPrizeRequest param) {
        // TODO 1. 检查是否需要回滚活动、人员、奖品
        if (!needRollbackStatus(param)) {
            return;
        }

        // TODO 2. 回滚状态
        rollbackStatus(param);

        // TODO 3. 检查是否需要回滚中奖者名单
        if (!needRollbackWinner(param)) {
            return;
        }

        // TODO 4. 回滚中奖者名单
        rollbackWinner(param);
    }

    private void rollbackWinner(DrawPrizeRequest param) {
        drawPrizeService.deleteRecords(param.getActivityId(), param.getPrizeId());
    }

    private boolean needRollbackWinner(DrawPrizeRequest param) {
        // TODO 判断中奖名单是否有当前奖品的数据
        QueryWrapper<WinningRecordDO> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda()
                    .eq(WinningRecordDO::getPrizeId, param.getPrizeId());
        return winningRecordMapper.selectCount(queryWrapper) > 0;
    }

    private void rollbackStatus(DrawPrizeRequest param) {
        activityStatusManager.handlerRollbackEvent(
                BeanTransUtil.transToConvertActivityStatusDTO(param, 2)
        );
    }

    private boolean needRollbackStatus(DrawPrizeRequest param) {
        // ? 由于扭转状态时活动、奖品和人员是处于一个事务的，如果一方修改了，则其他肯定修改了
        // TODO 1. 从数据库中获取活动奖品数据
        QueryWrapper<ActivityPrizeDO> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda()
                .eq(ActivityPrizeDO::getActivityId, param.getActivityId())
                .eq(ActivityPrizeDO::getPrizeId, param.getPrizeId());
        ActivityPrizeDO activityPrizeDO = activityPrizeMapper.selectOne(queryWrapper);

        // TODO 2. 校验活动奖品
        if (null == activityPrizeDO) {
            return false;
        }

        return activityPrizeDO.getStatus()
                .equalsIgnoreCase(ActivityPrizeStatusEnum.COMPLETED.name());
    }

    /**
     * 异步执行通知中奖者操作
     * @param winningRecordDOS 中奖者名单
     */
    private void syncExecute(List<WinningRecordDO> winningRecordDOS) {
        // ? 通过线程池 threadPoolTaskExecutor
        // TODO 1. 短信通知
        threadPoolTaskExecutor.execute(() -> sendMessage(winningRecordDOS));
        // TODO 2. 邮件通知
        threadPoolTaskExecutor.execute(() -> sendMail(winningRecordDOS));
    }

    /**
     * 发送邮件
     * @param winningRecordDOS 中奖者名单
     */
    private void sendMail(List<WinningRecordDO> winningRecordDOS) {
        // TODO 1. 校验参数
        if (CollectionUtils.isEmpty(winningRecordDOS)) {
            log.info("中奖列表为空，不用发邮件！");
            return;
        }

        // TODO 2. 逐个发送邮件
        for (WinningRecordDO winningRecordDO: winningRecordDOS) {
            String context = "Hi，" + winningRecordDO.getWinnerName() + "。恭喜你在"
                    + winningRecordDO.getActivityName() + "活动中获得"
                    + Objects.requireNonNull(ActivityPrizeTiersEnum.forName(winningRecordDO.getPrizeTier())).getMessage()
                    + "：" + winningRecordDO.getPrizeName() + "。获奖时间为"
                    + DateUtil.formatTime(winningRecordDO.getWinningTime()) + "，请尽快领 取您的奖励！";
            mailUtil.sendSampleMail(winningRecordDO.getWinnerEmail(),
                    "中奖通知", context);
        }
    }

    /**
     * 发送短信
     * @param winningRecordDOS 中奖者名单
     */
    private void sendMessage(List<WinningRecordDO> winningRecordDOS) {
        // TODO 1. 校验参数
        if (CollectionUtils.isEmpty(winningRecordDOS)) {
            log.info("中奖列表为空，不用发短信！");
            return;
        }

        // TODO 2. 逐个发送邮件
        Map<String, String> context = Map.of("code", "中奖了！！！");
        for (WinningRecordDO winningRecordDO: winningRecordDOS) {
            smsUtil.sendMessage("SMS_154950909",
                    winningRecordDO.getWinnerPhoneNumber().getValue(),
                    JacksonUtil.writeValueAsString(context));
        }
    }

    /**
     * 状态扭转
     * @param param 抽奖参数请求
     */
    private void statusConvert(DrawPrizeRequest param) {
        activityStatusManager.handlerEvent(
                BeanTransUtil.transToConvertActivityStatusDTO(param, 1)
        );
    }

//    private void statusConvert(DrawPrizeRequest param) {
//        // 问题：
//        // 1、活动状态扭转有依赖性，导致代码维护性差
//        // 2、状态扭转条件可能会扩展，当前写法，扩展性差，维护性差
//        // 3、代码的灵活性、扩展性、维护性极差
//        // 解决方案：设计模式（责任链设计模式、策略模式）
//
//        // 活动: RUNNING-->COMPLETED ??  全部奖品抽完之后才改变状态
//        // 奖品：INIT-->COMPLETED
//        // 人员列表: INIT-->COMPLETED
//
//        // 1 扭转奖品状态
//        // 查询活动关联的奖品信息
//        // 条件判断是否符合扭转奖品状态：判断当前状态是否 不是COMPLETED，如果不是，要扭转
//        // 才去扭转
//
//        // 2 扭转人员状态
//        // 查询活动关联的人员信息
//        // 条件判断是否符合扭转人员状态：判断当前状态是否 不是COMPLETED，如果不是，要扭转
//        // 才去扭转
//
//        // 4、扭转xxx状态
//
//        // 3 扭转活动状态（必须再扭转奖品状态之后完成）
//        // 查询活动信息
//        // 条件判断是否符合扭转活动状态：才改变状态
//        //      判断当前状态是否 不是COMPLETED，如果不是，
//        //      且全部奖品抽完之后，
//        //      （且xxx状态扭转）
//        // 才去扭转
//
//
//        // 4 更新活动完整信息缓存
//    }
}
